iT邦幫忙

2021 iThome 鐵人賽

DAY 24
2
Modern Web

ASP.NET Web Forms 入門 - 30天建立遊艇網頁專案後端及後台功能 C#系列 第 24

Day 24 - 依 Yachts 前台頁面分析拆解後,逐步建立 Overview 後台功能 - 動態新增欄位 - ASP.NET Web Forms C#

  • 分享至 

  • xImage
  •  

=x= 🌵 建立 Overview Manager - Content Page 後台頁面。


Yachts 前台 Overview 頁面分析 :

https://ithelp.ithome.com.tw/upload/images/20211008/201394874aYu0tZsPN.jpg

📌 進到 Yachts 頁面可以看到上方的不同型號輪播圖及左側型號側邊欄,這個部分的資料我們已經在 Yacht Manager - Content Page 做完後台了,而操作 Yachts 頁面內容後可以發現,上方的輪播頁及紅框的內容區塊,都是依據左側邊欄的型號決定,而紅框的內容區塊,則會因為內部上方的小導覽列操作改變內容,另外可以仔細操作會發現小導覽列的 Video 並不是每個型號都有,所以內容區塊才會把小導覽列包含進去,今天的文章將專注在製作 Overview 頁面內的資料,分別有 :

  1. 橘色區塊 : 主要圖文內容。
  2. 紫色區塊 : 尺寸標題及數值。
  3. 藍色區塊 : 尺寸附圖。
  4. 粉色區塊 : PDF 下載檔案連結及顯示文字。
  5. 綠色區塊 : Video 內的 YouTube 影片連結。


Overview Manager 後台頁面介紹 :

https://ithelp.ithome.com.tw/upload/images/20211008/201394877viKIiIUzB.jpg

🧠 左上區塊 - 型號下拉選單 + 尺寸附圖上傳 + PDF 上傳。

🧠 右上區塊 - 尺寸動態新增輸入欄位 + 下載標題 + Video 連結。

🧠 下半區塊 - 主要圖文編輯器。

  • 🌵 型號下拉選單用來決定是哪個型號的資料。


Overview Manager 後台實作 :

左上區塊頁面設計 :

1. 型號下拉選單設定直接使用精靈設定並將值設為 id,頁面設計參考如下

<h6>YachYacht Model :</h6>
<asp:DropDownList ID="DListModel" runat="server" DataSourceID="SqlDataSource1" DataTextField="yachtModel" DataValueField="id" AutoPostBack="True" Width="100%" Font-Bold="True" class="btn btn-outline-primary dropdown-toggle" OnSelectedIndexChanged="DListModel_SelectedIndexChanged" ></asp:DropDownList>
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:TayanaYachtConnectionString %>" SelectCommand="SELECT [yachtModel], [id] FROM [Yachts]"></asp:SqlDataSource>
<hr />
<h6>Dimensions Image :</h6>
<asp:Literal ID="LiteralDimImg" runat="server"></asp:Literal>
<div class="input-group my-3">
    <asp:FileUpload ID="DimImgUpload" runat="server" class="btn btn-outline-primary btn-block" />
    <asp:Button ID="BtnUploadDimImg" runat="server" Text="Upload" class="btn btn-primary" OnClick="BtnUploadDimImg_Click" />
</div>
<span class="badge badge-pill badge-warning text-dark">*Upload by No Choose File Could Clean File!</span>
<hr />
<h6>Downloads File :</h6>
<asp:Literal ID="PDFpreview" runat="server" ></asp:Literal>
<div class="input-group my-3">
    <asp:FileUpload ID="FileUpload" runat="server" class="btn btn-outline-primary btn-block" />
    <asp:Button ID="BtnUploadFile" runat="server" Text="Upload" class="btn btn-primary" OnClick="BtnUploadFile_Click" />
</div>
<span class="badge badge-pill badge-warning text-dark">*Upload by No Choose File Could Clean File!</span>


2. 在 <head> 內引用 PDF.js 用來在頁面預覽 PDF 內容

<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.9.359/pdf.min.js"></script>


右上區塊頁面設計 :

1. 表格加入新增欄位刪除欄位按鈕,頁面設計參考如下

<h6>Dimensions Text :</h6>
<table class="table table-hover">
    <thead>
        <tr class="table-info">
            <th>Item<asp:Button ID="AddRow" runat="server" Text="Add Row" class="btn btn-outline-primary btn-sm py-0 px-1 align-top mx-5" OnClick="AddRow_Click" /></th>
            <th>Value<asp:Button ID="DeleteRow" runat="server" Text="Delete" class="btn btn-outline-danger btn-sm py-0 px-1 align-top mx-5" OnClick="DeleteRow_Click" /></th>
        </tr>
    </thead>
    <tbody>
        <asp:Literal ID="LitDimensionsHtml" runat="server"></asp:Literal>
        <tr>
            <th>
                <p class="d-inline-block m-r-20">Dimensions Image</p>
            </th>
            <td>
                <asp:TextBox ID="TBoxDimImg" runat="server" type="text" class="form-control" ReadOnly="True"></asp:TextBox>
            </td>
        </tr>
        <tr>
            <th>
                <p class="d-inline-block m-r-20">Downloads Title</p>
            </th>
            <td class="table-info">
                <asp:TextBox ID="TBoxDLTitle" runat="server" type="text" class="form-control"></asp:TextBox>
            </td>
        </tr>
        <tr>
            <th>
                <p class="d-inline-block m-r-20">Downloads File</p>
            </th>
            <td>
                <asp:TextBox ID="TBoxDLFile" runat="server" type="text" class="form-control" ReadOnly="True"></asp:TextBox>
            </td>
        </tr>
        <tr>
            <th>
                <p class="d-inline-block m-r-20">Video URL</p>
            </th>
            <td class="table-info">
                <asp:TextBox ID="TBoxVideo" runat="server" type="text" class="form-control" TextMode="Url"></asp:TextBox>
            </td>
        </tr>
    </tbody>
    <tfoot>
        <tr>
            <th>
                <asp:Label ID="LabUpdateTitle" runat="server" Text="Click for Update"></asp:Label>
            </th>
            <td>
                <asp:Button ID="BtnUpdateDimensionsList" runat="server" Text="Update Dimensions List" class="btn btn-outline-primary btn-block" OnClick="BtnUpdateDimensionsList_Click" />
                <asp:Label ID="LabUpdateDimensionsList" runat="server" Text="*Upload Success!" ForeColor="green" class="d-flex justify-content-center" Visible="False"></asp:Label>
            </td>
        </tr>
    </tfoot>
</table>
  • 🌵 動態欄位只有尺寸標題及尺寸數值,下方為固定欄位,但 JSON 資料放在一起。


下半區塊頁面設計 :

1. 單純的所見及所得圖文編輯器,頁面設計參考如下

<h6>Main Content :</h6>
<CKEditor:CKEditorControl ID="CKEditorControl1" runat="server" BasePath="/Scripts/ckeditor/"
    Toolbar="Bold|Italic|Underline|Strike|Subscript|Superscript|-|RemoveFormat
        NumberedList|BulletedList|-|Outdent|Indent|-|JustifyLeft|JustifyCenter|JustifyRight|JustifyBlock|-|BidiLtr|BidiRtl
        /
        Styles|Format|Font|FontSize
        TextColor|BGColor
        Link|Image"
    Height="400px">
</CKEditor:CKEditorControl>
<asp:Label ID="LabUploadMainContent" runat="server" Visible="False" ForeColor="#009933" class="d-flex justify-content-center"></asp:Label>
<asp:Button ID="BtnUploadMainContent" runat="server" Text="Upload Overview Content" class="btn btn-outline-primary btn-block mt-3" OnClick="BtnUploadMainContent_Click" />
  • 🌵 動態欄位只有尺寸標題及尺寸數值,下方為固定欄位,但 JSON 資料放在一起。


後置程式碼方法內容 :

1. 在 Page_Load 加入畫面資料讀取及渲染等相關方法如下

//宣告 List 方便用 Add 依序添加欄位資料
private List<RowData> saveRowList = new List<RowData>();
protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack) {
        ckfinderSetPath();
        DListModel.DataBind(); //先綁定取得選取值
        loadContent(); //取得尺寸欄位區外的主要內容
        loadRowList(); //取得尺寸欄位內容
        renderRowList(); //渲染尺寸欄位內容
    }
}

private void ckfinderSetPath()
{
    FileBrowser fileBrowser = new FileBrowser();
    fileBrowser.BasePath = "/ckfinder";
    fileBrowser.SetupCKEditor(CKEditorControl1);
}

//欄位表 JSON 資料
public class RowData
{
    public string SaveItem { get; set; }
    public string SaveValue { get; set; }
}
  • 🌵 List<T> 為欄位 JSON 資料用。


2. 建立取得尺寸欄位外的主要內容方法 loadContent(); 方法邏輯內容如下

private void loadContent()
{
    //清空畫面資料
    TBoxVideo.Text = "";
    TBoxDLTitle.Text = "";
    TBoxDimImg.Text = "";
    TBoxDLFile.Text = "";
    PDFpreview.Text = "";
    LiteralDimImg.Text = "";

    //依下拉選單選取型號取出資料
    string selectModel_id = DListModel.SelectedValue;
    SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["TayanaYachtConnectionString"].ConnectionString);
    string sql = "SELECT * FROM Yachts WHERE id = @selectModel_id";
    SqlCommand command = new SqlCommand(sql, connection);
    command.Parameters.AddWithValue("@selectModel_id", selectModel_id);
    connection.Open();
    SqlDataReader reader = command.ExecuteReader();
    while (reader.Read()) {
        //渲染畫面
        CKEditorControl1.Text = HttpUtility.HtmlDecode(reader["overviewContentHtml"].ToString());
        string imgPathStr = reader["overviewDimensionsImgPath"].ToString();
        string filePathStr = reader["overviewDownloadsFilePath"].ToString();
        TBoxDimImg.Text = imgPathStr;
        LiteralDimImg.Text = $"<img alt='Dimensions Image' class='img-thumbnail rounded mx-auto d-block' src='/Tayanahtml/upload/Images/{imgPathStr}' Width='250px'/>";
        TBoxDLFile.Text = filePathStr;
        PDFpreview.Text = $"<object type='application/pdf' data='/Tayanahtml/upload/files/{filePathStr}' width='250px' height='385px' class='rounded mx-auto d-block' ></object>";
    }
    connection.Close();
}
  • 🌵 PDF 預覽用 <object> 標籤放入畫面。


3. 建立取得尺寸欄位內容方法 loadRowList(); 方法邏輯內容如下

private void loadRowList()
{
    //依選取型號取得欄位資料更新 List<T>
    string selectModel_id = DListModel.SelectedValue;
    SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["TayanaYachtConnectionString"].ConnectionString);
    string sql = "SELECT * FROM Yachts WHERE id = @selectModel_id";
    SqlCommand command = new SqlCommand(sql, connection);
    command.Parameters.AddWithValue("@selectModel_id", selectModel_id);
    connection.Open();
    SqlDataReader reader = command.ExecuteReader();
    if (reader.Read()) {
        string loadJson = HttpUtility.HtmlDecode(reader["overviewDimensionsJSON"].ToString());
        saveRowList = JsonConvert.DeserializeObject<List<RowData>>(loadJson);
    }
    connection.Close();
}
  • 🌵 讀取資料與渲染畫面拆開,方便動態增刪欄位功能製作。


4. 建立渲染尺寸欄位畫面方法 renderRowList(); 方法邏輯內容如下

private void renderRowList()
{
    if (saveRowList?.Count > 0) {
        string addRowHtmlStr = "";
        int index = 0;
        //從 List<T> 載入資料
        foreach (var item in saveRowList) {
            if (index == 0) {
                TBoxVideo.Text = item.SaveValue;
            }
            if (index == 1) {
                TBoxDLTitle.Text = item.SaveValue;
            }
            if (index > 1) {
                addRowHtmlStr += $"<tr class='table-info'><th><input id='item{index}' name='item{index}' type='text' class='form-control font-weight-bold' value='{item.SaveItem}' /></th><td><input id='value{index}' name='value{index}' type='text' class='form-control' value='{item.SaveValue}' /></td></tr>";
            }
            index++;
        }
        //渲染畫面
        LitDimensionsHtml.Text = addRowHtmlStr;
    }
}
  • 🌵 JSON 資料前兩筆是 Video 連結及 PDF 顯示文字。

  • 🌵 尺寸動態增刪欄位用 HTML 的 <input> 標籤並賦予 id。



分別建立按鈕事件功能 :

1. 遊艇型號下拉選單選項改變時觸發內容

protected void DListModel_SelectedIndexChanged(object sender, EventArgs e)
{
    //隱藏上傳成功提示
    LabUploadMainContent.Visible = false;
    LabUpdateDimensionsList.Visible = false;
    //渲染畫面
    loadContent();
    loadRowList();
    renderRowList();
}


2. 建立尺寸附圖上傳 Upload 按鈕 OnClick 功能如下

protected void BtnUploadDimImg_Click(object sender, EventArgs e)
{
    string savePath = Server.MapPath("~/Tayanahtml/upload/Images/");
    string fileName = DimImgUpload.FileName;
    string selectModel_id = DListModel.SelectedValue;
    SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["TayanaYachtConnectionString"].ConnectionString);
    //有選檔案才可上傳,沒選檔案則執行清空
    if (DimImgUpload.HasFile) {
        string sqlDel = "SELECT overviewDimensionsImgPath FROM Yachts WHERE id = @selectModel_id";
        SqlCommand commandDel = new SqlCommand(sqlDel, connection);
        commandDel.Parameters.AddWithValue("@selectModel_id", selectModel_id);

        //刪除舊圖檔
        connection.Open();
        SqlDataReader reader = commandDel.ExecuteReader();
        if (reader.Read()) {
            string delFileName = reader["overviewDimensionsImgPath"].ToString();
            if (!String.IsNullOrEmpty(delFileName)) {
                File.Delete(savePath + delFileName);
            }
        }
        connection.Close();

        //儲存圖片檔案及圖片名稱
        //檢查專案資料夾內有無同名檔案,有同名就加流水號
        DirectoryInfo directoryInfo = new DirectoryInfo(savePath);
        string[] fileNameArr = fileName.Split('.');
        int count = 0;
        foreach (var fileItem in directoryInfo.GetFiles()) {
            if (fileItem.Name.Contains(fileNameArr[0])) {
                count++;
            }
        }
        fileName = fileNameArr[0] + $"({count + 1})." + fileNameArr[1];
        DimImgUpload.SaveAs(savePath + fileName);

        //更新資料
        string sql = "UPDATE Yachts SET overviewDimensionsImgPath = @fileName WHERE id = @selectModel_id";
        SqlCommand command = new SqlCommand(sql, connection);
        command.Parameters.AddWithValue("@fileName", fileName);
        command.Parameters.AddWithValue("@selectModel_id", selectModel_id);
        connection.Open();
        command.ExecuteNonQuery();
        connection.Close();

        //渲染畫面
        loadContent();
        loadRowList();
        renderRowList();
    }
    else {
        //沒選檔案點上傳則清空
        string sql = "UPDATE Yachts SET overviewDimensionsImgPath = @imgPath WHERE id = @selectModel_id";
        SqlCommand command = new SqlCommand(sql, connection);
        command.Parameters.AddWithValue("@imgPath", "");
        command.Parameters.AddWithValue("@selectModel_id", selectModel_id);
        connection.Open();
        command.ExecuteNonQuery();
        connection.Close();

        //渲染畫面
        loadContent();
        loadRowList();
        renderRowList();
    }
}
  • 🌵 用提示告知使用者沒選圖片點上傳會清空檔案,這樣可以不用寫清除功能。


3. 建立 PDF 檔上傳 Upload 按鈕 OnClick 功能如下

protected void BtnUploadFile_Click(object sender, EventArgs e)
{
    string savePath = Server.MapPath("~/Tayanahtml/upload/files/");
    string fileName = FileUpload.FileName;
    string selectModel_id = DListModel.SelectedValue;
    SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["TayanaYachtConnectionString"].ConnectionString);
    //有選檔案才可上傳
    if (FileUpload.HasFile) {
        //先刪除舊檔
        string sqlDel = "SELECT overviewDownloadsFilePath FROM Yachts WHERE id = @selectModel_id";
        SqlCommand commandDel = new SqlCommand(sqlDel, connection);
        commandDel.Parameters.AddWithValue("@selectModel_id", selectModel_id);
        connection.Open();
        SqlDataReader reader = commandDel.ExecuteReader();
        if (reader.Read()) {
            string delFileName = reader["overviewDownloadsFilePath"].ToString();
            if (!String.IsNullOrEmpty(delFileName)) {
                File.Delete(savePath + delFileName);
            }
        }
        connection.Close();

        //儲存上傳檔案
        //檢查專案資料夾內有無同名檔案,有同名就加流水號
        DirectoryInfo directoryInfo = new DirectoryInfo(savePath);
        int count = 0;
        foreach (var fileItem in directoryInfo.GetFiles()) {
            if (fileItem.Name.Contains(fileName)) {
                count++;
            }
        }
        string[] fileNameArr = fileName.Split('.');
        fileName = fileNameArr[0] + $"({count + 1})." + fileNameArr[1];
        FileUpload.SaveAs(savePath + fileName);

        //更新資料庫資料
        string sql = "UPDATE Yachts SET overviewDownloadsFilePath = @fileName WHERE id = @selectModel_id";
        SqlCommand command = new SqlCommand(sql, connection);
        command.Parameters.AddWithValue("@fileName", fileName);
        command.Parameters.AddWithValue("@selectModel_id", selectModel_id);
        connection.Open();
        command.ExecuteNonQuery();
        connection.Close();

        //渲染畫面
        loadContent();
        loadRowList();
        renderRowList();
    }
    else {
        //沒選檔案則清空資料
        string sqlDelPath = "UPDATE Yachts SET overviewDownloadsFilePath = @fileName WHERE id = @selectModel_id";
        SqlCommand commandDelPath = new SqlCommand(sqlDelPath, connection);
        commandDelPath.Parameters.AddWithValue("@fileName", "");
        commandDelPath.Parameters.AddWithValue("@selectModel_id", selectModel_id);
        connection.Open();
        commandDelPath.ExecuteNonQuery();
        connection.Close();

        //渲染畫面
        loadContent();
        loadRowList();
        renderRowList();
    }
}
  • 🌵 同上,沒選圖片點上傳會清空檔案當作清除功能。


4. 動態新增欄位按鈕功能利用增加空白 List 資料,配合前面的讀取及渲染方法達成

protected void AddRow_Click(object sender, EventArgs e)
{
    //將 JSON 資料載入 List<T>
    loadRowList();
    //增加新欄位
    saveRowList.Add(new RowData { SaveItem = "", SaveValue = "" });
    //更新資料庫資料
    uploadRowList();
    //渲染畫面
    renderRowList();
    //將畫面移至出現上傳按鈕處
    Page.SetFocus(BtnUpdateDimensionsList);
}
  • 🌵 使用內建方法 Page.SetFocus(); 在刷新頁面時跳至指定區域。


5. 新增上方出現的更新尺寸欄位資料至資料庫的 uploadRowList(); 方法

private void uploadRowList()
{
    //先更新 List<T> 前兩個資料 Video 及 Download File
    saveRowList[0].SaveValue = TBoxVideo.Text;
    saveRowList[1].SaveValue = TBoxDLTitle.Text;
    //更新 List<T> 資料,欄位內容用 Request.Form 
    for (int i = 2; i < saveRowList.Count; i++) {
        saveRowList[i].SaveItem = Request.Form[$"item{i}"];
        saveRowList[i].SaveValue = Request.Form[$"value{i}"];
    }

    //依選取型號更新資料庫資料
    string selectModel_id = DListModel.SelectedValue;
    //將 List<T> 資料轉為 JSON 格式字串
    string saveRowListJsonStr = JsonConvert.SerializeObject(saveRowList);
    SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["TayanaYachtConnectionString"].ConnectionString);
    string sql = "UPDATE Yachts SET overviewDimensionsJSON = @saveRowListJsonStr WHERE id = @selectModel_id";
    SqlCommand command = new SqlCommand(sql, connection);
    command.Parameters.AddWithValue("@saveRowListJsonStr", saveRowListJsonStr);
    command.Parameters.AddWithValue("@selectModel_id", selectModel_id);
    connection.Open();
    command.ExecuteNonQuery();
    connection.Close();
}
  • 🌵 傳統 HTML 標籤的 <input> 內容。

  • 🌵 欄位區的 3 個按鈕都要配合進行刷新資料庫內容。


6. 動態刪除欄位按鈕功能利用移除最末筆 List 資料,配合前面的讀取及渲染方法達成

protected void DeleteRow_Click(object sender, EventArgs e)
{
    //將 JSON 資料載入 List<T>
    loadRowList();
    //更新資料庫資料
    uploadRowList();
    //刪除尺寸欄位最末欄
    saveRowList.RemoveAt(saveRowList.Count - 1);
    //更新資料庫資料
    uploadRowList();
    //渲染表格畫面
    renderRowList();
    //將畫面移至出現上傳按鈕處
    Page.SetFocus(BtnUpdateDimensionsList);
}


7. 建立更新尺寸欄位 Update Dimensions List 按鈕的 OnClick 功能

protected void BtnUpdateDimensionsList_Click(object sender, EventArgs e)
{
    //將 JSON 資料載入 List<T>
    loadRowList();
    //更新資料庫資料
    uploadRowList();
    //渲染表格畫面
    renderRowList();
    //渲染上傳成功提示
    DateTime nowtime = DateTime.Now;
    LabUpdateDimensionsList.Visible = true;
    LabUpdateDimensionsList.Text = "*Upload Success! - " + nowtime.ToString("G");
}


8. 建立圖文編輯區上傳 Upload Overview Content 按鈕 OnClick 功能

protected void BtnUploadMainContent_Click(object sender, EventArgs e)
{
    //將文字編輯器 HTML 內容轉為 HTML 字元編碼
    string mainContentHtmlStr = HttpUtility.HtmlEncode(CKEditorControl1.Text);
    //依下拉選單選取型號存入型號介紹主要圖文內容
    string selectModel_id = DListModel.SelectedValue;
    SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["TayanaYachtConnectionString"].ConnectionString);
    string sql = "UPDATE Yachts SET overviewContentHtml = @mainContentHtmlStr WHERE id = @selectModel_id";
    SqlCommand command = new SqlCommand(sql, connection);
    command.Parameters.AddWithValue("@mainContentHtmlStr", mainContentHtmlStr);
    command.Parameters.AddWithValue("@selectModel_id", selectModel_id);
    connection.Open();
    command.ExecuteNonQuery();
    connection.Close();

    //渲染上傳成功提示
    DateTime nowtime = DateTime.Now;
    LabUploadMainContent.Visible = true;
    LabUploadMainContent.Text = "*Upload Success! - " + nowtime.ToString("G");
}

9. 模擬頁面後測試功能,Overview Manager 後台完成 ~



本日總結 :

📢 本日的重點在重複使用的方法抽出來使用的好處,及動態增刪欄位的作法,由於這個後台並非多人使用,因此使用連接資料庫刷新的作法,也有看到用 ViewState 或其它方式實作的方法,可以自行研究;後續文章因為實作內容較多,並且接近收尾,都會如本文盡量將完整程式碼放上來,而分層架構處理會在最後一天文章說明實作可以精修的部分。

👀 大神系列文章 ViewState 介紹參考 : 91之ASP.NET由淺入深 不負責講座

  • 明日將介紹製作 Yachts - Master Page 主版頁面的相關細節。

上一篇
Day 23 - 將 Yacht Manager 後台儲存資料提取後,送至前台渲染首頁 Home 頁面 (下) - 新聞圖卡區 - ASP.NET Web Forms C#
下一篇
Day 25 - 將 Yacht Manager 後台儲存資料提取後,送至前台渲染 Yachts 版面重覆區塊 - 前台 Master Page 製作 - ASP.NET Web Forms C#
系列文
ASP.NET Web Forms 入門 - 30天建立遊艇網頁專案後端及後台功能 C#30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言